#include "GlfwInit.h"

#define GLFW_STATIC

#include<iostream>
#include<fstream>
#include<sstream>

#define STB_IMAGE_IMPLEMENTATION

#include "src/utils/stb_image.h"
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "src/camera/Camera.h"
#include "src/material/Material.h"
#include "src/utils/Utils.h"
#include "src/light/LightDirectional.h"
#include "src/light/LightPoint.h"
#include "src/light/LightSpot.h"
#include "src/model/Mesh.h"
//#include "src/model/Model.h"
using namespace std;
#pragma region Model Data
float vertices_[] = {
        // positions          // normals           // texture coords
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
        0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
        0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
        -0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,

        -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
        0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
        -0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,

        -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        -0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        -0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
        -0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,

        0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
        0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
        0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
        0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
        0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
        0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
        0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
        0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
        -0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,

        -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
        0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
        0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
        -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
        -0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
glm::vec3 cubePositions[] = {
        glm::vec3(0.0f, 0.0f, 0.0f),
        glm::vec3(2.0f, 5.0f, -15.0f),
        glm::vec3(-1.5f, -2.2f, -2.5f),
        glm::vec3(-3.8f, -2.0f, -12.3f),
        glm::vec3(2.4f, -0.4f, -3.5f),
        glm::vec3(-1.7f, 3.0f, -7.5f),
        glm::vec3(1.3f, -2.0f, -2.5f),
        glm::vec3(1.5f, 2.0f, -2.5f),
        glm::vec3(1.5f, 0.2f, -1.5f),
        glm::vec3(-1.3f, 1.0f, -1.5f)
};
#pragma endregion

#pragma region Declare Camera

Utils utils = Utils();

Camera camera(
        glm::vec3(0, 0, 3.0f),
        glm::radians(0.0f),
        glm::radians(180.0f),
        glm::vec3(0, 1.0f, 0),
        0.0005,
        0.0005,
        45.0);

void CameraMove(GLFWwindow *w, double xPos, double yPos) {
    camera.Movement(xPos, yPos);
}

#pragma endregion

#pragma region Declare lightDirection
LightDirectional lightDirect(
        glm::vec3(0.0f, 5.0f, 0.0f),
        glm::vec3(glm::radians(125.0f), glm::radians(45.0f), 0),
        glm::vec3(2.0f, 2.0f, 2.0f));
LightPoint lightPoint1(
        glm::vec3(1.0f, 0.0f, 0.0f),
        glm::vec3(glm::radians(45.0f), 0, 0),
        glm::vec3(1.0f, 0.0f, 0.0f));
LightSpot lightSpot(
        glm::vec3(0.0f, 5.0f, 0.0f),
        glm::vec3(glm::radians(90.0f), 0, 0),
        glm::vec3(1.0f, 0.0f, 0.0f));
#pragma endregion

GlfwInit::GlfwInit() {

}

int GlfwInit::init() {

#pragma region Open Window
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    GLFWwindow *window = glfwCreateWindow(800, 800, "two", NULL, NULL);
    if (window == NULL) {
        printf("create window error");
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    glfwSetCursorPosCallback(window, CameraMove);

    if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
        printf("init glad error");
        glfwTerminate();
        return -1;
    }
    glViewport(50, 50, 800, 800);
    //深度测试 -- 一个片段的深度值和深度缓存中的内容作比较，测试通过则更新深度缓存的内容，否作丢弃。
    glEnable(GL_DEPTH_TEST);
    //禁止深度测试写入
    //glDepthMask(GL_FALSE);
    //深度测试函数
    //glDepthFunc();
#pragma endregion

    Shader shader = Shader();
    unsigned int ID = shader.loadShader(utils.GetResourceDir().append("\\resource\\glsl\\vertex.glsl"),
                                        utils.GetResourceDir().append("\\resource\\glsl\\frag.glsl"));
    useImage(GL_TEXTURE0, GL_RGBA, utils.GetResourceDir().append("\\resource\\container2.png"));
    useImage(GL_TEXTURE0 + 1, GL_RGBA, utils.GetResourceDir().append("\\resource\\container2_specular.png"));
    Material material = Material(&shader,
                                 glm::vec3(utils.RGBToFloat(255, 255, 255)),
                                 glm::vec3(utils.RGBToFloat(255, 0, 0)),
                                 glm::vec3(utils.RGBToFloat(124, 252, 0)),
                                 glm::vec3(utils.RGBToFloat(255, 255, 255)),
                                 32.0f);
    Mesh cube(vertices_);
//    Model m("D:\\OpenGL\\model\\nanosuit.obj");
    glm::mat4 model;
    glm::mat4 view;
    glm::mat4 project;
    while (!glfwWindowShouldClose(window)) {
        handleInput(window);

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        //深度测试开启时 每一帧都需要清楚深度缓存
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        project = glm::perspective(glm::radians(camera.FOV), (float) 800 / (float) 800, 0.1f, 100.0f);

        view = camera.GetViewMatrix();
        glm::vec3 lightColor;
        lightColor.x = sin(glfwGetTime() * 2.0f);
        lightColor.y = sin(glfwGetTime() * 0.7f);
        lightColor.z = sin(glfwGetTime() * 1.3f);

        glUseProgram(shader.ID);

        for (int i = 0; i < 1; i++) {

            model = glm::translate(glm::mat4(1.0f), cubePositions[i]);

            glUniformMatrix4fv(glGetUniformLocation(ID, "model"), 1, false, glm::value_ptr(model));
            glUniformMatrix4fv(glGetUniformLocation(ID, "view"), 1, false, glm::value_ptr(view));
            glUniformMatrix4fv(glGetUniformLocation(ID, "project"), 1, false, glm::value_ptr(project));

            glUniform3f(glGetUniformLocation(ID, "objColor"), 1.0f, 1.0, 1.0f);
            glUniform3f(glGetUniformLocation(ID, "ambientColor"), 0.0f, 0.0f, 0.0f);
            glUniform3f(glGetUniformLocation(ID, "cameraPos"), camera.Position.x, camera.Position.y, camera.Position.z);

            glUniform3f(glGetUniformLocation(ID, "lightDiractional.pos"), lightDirect.position.x,
                        lightDirect.position.y, lightDirect.position.z);
            glUniform3f(glGetUniformLocation(ID, "lightDiractional.dirToLight"), lightDirect.direction.x,
                        lightDirect.direction.y, lightDirect.direction.z);
            glUniform3f(glGetUniformLocation(ID, "lightDiractional.color"), lightDirect.color.x, lightDirect.color.y,
                        lightDirect.color.z);

            glUniform3f(glGetUniformLocation(ID, "lightPoint1.pos"), lightPoint1.position.x, lightPoint1.position.y,
                        lightPoint1.position.z);
            glUniform3f(glGetUniformLocation(ID, "lightPoint1.dirToLight"), lightPoint1.direction.x,
                        lightPoint1.direction.y, lightPoint1.direction.z);
            glUniform3f(glGetUniformLocation(ID, "lightPoint1.color"), lightPoint1.color.x, lightPoint1.color.y,
                        lightPoint1.color.z);
            glUniform1f(glGetUniformLocation(ID, "lightPoint1.constant"), lightPoint1.constant);
            glUniform1f(glGetUniformLocation(ID, "lightPoint1.linear"), lightPoint1.linear);
            glUniform1f(glGetUniformLocation(ID, "lightPoint1.quadratic"), lightPoint1.quadratic);

            glUniform1f(glGetUniformLocation(ID, "lightSpot.constant"), lightSpot.constant);
            glUniform1f(glGetUniformLocation(ID, "lightSpot.linear"), lightSpot.linear);
            glUniform1f(glGetUniformLocation(ID, "lightSpot.quadratic"), lightSpot.quadratic);
            glUniform1f(glGetUniformLocation(ID, "lightSpot.cosInThy"), lightSpot.cosInPhy);
            glUniform1f(glGetUniformLocation(ID, "lightSpot.cosOutThy"), lightSpot.cosOutPhy);

            material.shader->SetUniform3f(ID, "material.ambient", material.ambient);
            material.shader->SetUniform1i(ID, "material.diffuse", material.DIFFUSE);
            material.shader->SetUniform1i(ID, "material.specular", material.SPECULAR);
            material.shader->SetUniform1i(ID, "material.emission", material.EMISSION);
            material.shader->SetUniform3f(ID, "material.emissionColor", material.emissionColor);
            material.shader->SetUniform3f(ID, "material.specularColor", material.specularColor);
            material.shader->SetUniform3f(ID, "material.diffuseColor", material.diffuseColor);
            material.shader->SetUniform1f(ID, "material.shiness", material.shiness);

            cube.Draw(&shader);
//            m.Draw(&shader);
        }

        glfwPollEvents();
        glfwSwapBuffers(window);

        camera.UpdateCameraPosition();
    }

    glfwTerminate();
    return 0;
}


unsigned int GlfwInit::useImage(GLenum glenum, GLint color, std::string imagPath) {
    unsigned int texture;
    glGenTextures(1, &texture);
    glActiveTexture(glenum);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    stbi_set_flip_vertically_on_load(true);
    int width, height, channel;
    unsigned char *data = stbi_load(imagPath.data(), &width, &height, &channel, 0);

    if (data) {
        glTexImage2D(GL_TEXTURE_2D, 0, color, width, height, 0, color, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);
    } else {
        cout << "failed to load texture" << endl;
    }
    stbi_image_free(data);
    return texture;
}

void GlfwInit::handleInput(GLFWwindow *w) {
    if (glfwGetKey(w, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
        glfwSetWindowShouldClose(w, true);
    }

    if (glfwGetKey(w, GLFW_KEY_A) == GLFW_PRESS) {
        camera.SpeedX = -1.0f;
    } else if (glfwGetKey(w, GLFW_KEY_D) == GLFW_PRESS) {
        camera.SpeedX = 1.0f;
    } else {
        camera.SpeedX = 0.0f;
    }

    if (glfwGetKey(w, GLFW_KEY_W) == GLFW_PRESS) {
        camera.SpeedZ = 1.0f;
    } else if (glfwGetKey(w, GLFW_KEY_S) == GLFW_PRESS) {
        camera.SpeedZ = -1.0f;
    } else {
        camera.SpeedZ = 0.0f;
    }

    if (glfwGetKey(w, GLFW_KEY_Q) == GLFW_PRESS) {
        camera.SpeedY = 1.0f;
    } else if (glfwGetKey(w, GLFW_KEY_E) == GLFW_PRESS) {
        camera.SpeedY = -1.0f;
    } else {
        camera.SpeedY = 0.0f;
    }
}
